package cn.openea.perpartner.cms.utils;


import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.action.PdfAction;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.*;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;
import com.itextpdf.layout.properties.VerticalAlignment;
import lombok.AllArgsConstructor;
import lombok.Data;
import cn.openea.perpartner.cms.bean.FieldData;
import cn.openea.perpartner.cms.bean.PdfDataSource;
import cn.openea.perpartner.cms.bean.properties.DType;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.time.Duration;
import java.time.Instant;
import java.util.List;

/**
 * @Name: 创建PDF文档并导入到指定地址
 * @desc: 为了保证性能，这里不建议一次性生成的数据行超过20条
 * @author: Mr.Yun
 * @Created: 2022/8/23 1:36
 */
@Data
@AllArgsConstructor
public class PdfModel {

    // 需要传入的参数，PDF的创建由该参数决定
    private PdfDataSource dataDefine;

    /**
     * 创建PDF文档并导入到指定地址
     * @param outpath 文件生成地址
     * @throws IOException
     */
    public void createPDF(String outpath) throws IOException {
        Instant start = Instant.now();
        PdfWriter writer = new PdfWriter(outpath);
        PdfDocument pdf = new PdfDocument(writer);
        PageSize a4 = new PageSize(PageSize.A4);
        // 判断是否需要横向纸张
        if(dataDefine.isRotate()){
            a4 = new PageSize(PageSize.A4.rotate());
        }

        // 建立pdf文档对象
        Document document = new Document(pdf, a4, false);
        // 默认使用宋体
        PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
        if(!"default".equals(dataDefine.getFont())){
            font = PdfFontFactory.createFont(dataDefine.getFont());//使用指定的字体文件
        }

        // 第1部分：设置生成水印的部分
        TextWaterMarkHandler textWaterMarkHandler = new TextWaterMarkHandler("ParPartner", font);
        pdf.addEventHandler(PdfDocumentEvent.INSERT_PAGE, textWaterMarkHandler);

        // 第2部分：添加顶部logo条
        Image logoimg = new Image(getImageDataByPath("/static/image/logo.png")).setWidth(UnitValue.createPercentValue(100));
        document.add(logoimg);

        // 第3部分：建立表单的总标头部分
        document.add(createOneCellTable(dataDefine.getTitleText(), true, 17, TextAlignment.CENTER, font));

        // 第4部分：建立表单的标题字段
        List<String> headTag = dataDefine.getHeadTag();
        Table table = new Table(headTag.size());
        table.setWidth(UnitValue.createPercentValue(100));//100%宽度
        for (int i = 0; i < headTag.size(); i++) {
            // 这里对标题进行循环，添加到 table 中去
            table.addHeaderCell(createTextCell(headTag.get(i), true, font));
        }

        // 第5部分：从 dataList 中获取数据行，建立表单的数据行
        List<List<FieldData>> dataList = dataDefine.getDataList();
        // 建立一条空数据
        FieldData emptyFieldData = new FieldData("", DType.TEXT, "");
        for (int i = 0; i < dataList.size(); i++) {
            List<FieldData> rowdata = dataList.get(i);
            int fieldcount = rowdata.size();
            // 在这里要按标题头的长度生成子表行字段，默认要扩充到与标题头的长度，所以这里按标题头的长度循环
            for (int j = 0; j < headTag.size(); j++) {
                if(j < fieldcount) {
                    // 如果行数据的内容可以取到
                    table.addCell(doCreateCell(rowdata.get(j), false, font));
                } else {
                    // 如果行数据的列字段不够，则使用空数据进行补充
                    table.addCell(doCreateCell(emptyFieldData, false, font));
                }
            }
        }
        document.add(table);

        // 第6部分：建立表单的尾部说明文字
        document.add(createOneCellTable(dataDefine.getTailText(), false, 10, TextAlignment.LEFT, font));

        // 第7部分：加盖公司公章部分
        Image chapterimg = new Image(getImageDataByPath("/static/image/chapter.png")).setHeight(90).setWidth(90);
        // 这里需要调整图片的样式
        document.add(new Paragraph().add(chapterimg).setTextAlignment(TextAlignment.RIGHT).setMarginTop(-45).setPaddingRight(30));

        // 第8部分：生成页码
        createPageNumber(pdf, document, a4, font);
        document.close();

        Instant end = Instant.now();
        System.out.println("执行创建PDF耗时(milliseconds) = " + Duration.between(start, end).toMillis());
    }

    /**
     * 从指定路径获取图片文件，包含图片在jar包内
     * @param imagepath 图片的指定路径
     * @return
     * @throws IOException
     */
    private ImageData getImageDataByPath(String imagepath) throws IOException {
        String filepath = this.getClass().getResource(imagepath).getPath();
        System.out.println("filepath = " + filepath);
        if(filepath.contains(".jar!/BOOT-INF/")){
            // jar包的方式
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(imagepath);
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024*4];
            int n = 0;
            while (-1 != (n = inputStream.read(buffer))) {
                output.write(buffer, 0, n);
            }
            return ImageDataFactory.create(output.toByteArray());
        } else {
            // IDE中执行
            return ImageDataFactory.create(filepath);
        }
    }


    /**
         * 依据DType枚举类型自动判断创建哪一类型的单元格
         * @param fieldData 单元格的字段内容
         * @param isHeader 是否标题头
         * @param font 使用的字体
         * @return
         * @throws MalformedURLException
         */
        private Cell doCreateCell(FieldData fieldData, boolean isHeader, PdfFont font) throws IOException {
            int width = 80; int height = 80;
        if(DType.IMAGE == fieldData.getDType()){
            // 图片类型
            return createImageCell(fieldData.getValueText(), width, height);
        } else if(DType.TEXTURL == fieldData.getDType()){
            // 文本带A标签的URL
            return createUrlCell(fieldData, font);
        } else {
            // 如果以上都不是，则默认为文本类型
            return createTextCell(fieldData.getValueText(), isHeader, font);
        }
    }

    /**
     * 创建文本域中带点击链接的单元格，并默认居中对齐
     * @param fieldData 单元格的字段内容
     * @param font 使用的字体
     * @return
     */
    private Cell createUrlCell(FieldData fieldData, PdfFont font) {
        Paragraph p = new Paragraph().setTextAlignment(TextAlignment.CENTER);
        // 建立访问热点URL
        PdfAction action = PdfAction.createURI(fieldData.getUrl());
        Link link = (Link) new Link(fieldData.getValueText(), action).setFont(font).setUnderline();
        p.add(link);
        return new Cell().setVerticalAlignment(VerticalAlignment.MIDDLE).add(p);
    }

    /**
     * 创建图片单元格，并默认居中对齐
     * @param valueText 图片地址
     * @param width 图片宽度
     * @param height 图片高度
     * @return
     * @throws MalformedURLException
     */
    private Cell createImageCell(String valueText, int width, int height) throws IOException {
        Paragraph paragraph = new Paragraph().setTextAlignment(TextAlignment.CENTER);
        // 图片地址为空时，使用文本返回
        if(null == valueText || "".equals(valueText)){
            return new Cell().setVerticalAlignment(VerticalAlignment.MIDDLE).add(paragraph.add(""));
        }
        Image image = null;
        try {
            // 尝试使用原来提供的 valueText 地址创建图片，如果失败，则使用系统默认的图片
            image = new Image(ImageDataFactory.create(valueText));
        } catch (Exception e) {
            // 如果提供的链接有问题，则使用系统自带的404图片 /static/image/404.jpg
            image = new Image(getImageDataByPath("/static/image/404.jpg"));
        }
        image.setWidth(width).setHeight(height);
        paragraph.add(image);
        return new Cell().setVerticalAlignment(VerticalAlignment.MIDDLE).add(paragraph);
    }

    /**
     * 创建文本单元格，并默认居中对齐
     * @param text 单元格内的文字
     * @param isHeader 是否标题头，如果是的话 字体号13，并且加上一定的背景颜色
     * @param font 使用的字体
     * @return
     */
    private Cell createTextCell(String text, boolean isHeader, PdfFont font) {
        Paragraph paragraph = new Paragraph(text).setFont(font).setTextAlignment(TextAlignment.CENTER);
        if(isHeader){
            paragraph.setBold().setFontSize(13).setBackgroundColor(new DeviceRgb(221, 234, 238));
        }
        Cell cell = new Cell().setVerticalAlignment(VerticalAlignment.MIDDLE).add(paragraph);
        return cell;
    }

    /**
     * 创建一个单元格占100%的Table表单
     * @param text 单元格内的文字
     * @param isBold 是否加粗
     * @param fontSize 字号
     * @param alignment 对齐方式
     * @param font 使用的字体
     * @return
     */
    private Table createOneCellTable(String text, boolean isBold, int fontSize, TextAlignment alignment, PdfFont font) {
        // 新建1个单元格100%宽度的表单
        Table table = new Table(1).setWidth(UnitValue.createPercentValue(100));
        Paragraph paragraph = new Paragraph().add(text).setFont(font).setFontSize(fontSize).setTextAlignment(alignment);
        if(isBold){
            // 需要加粗
            paragraph.setBold();
        }
        table.addCell(new Cell().setVerticalAlignment(VerticalAlignment.MIDDLE).add(paragraph));
        return table;
    }

    /**
     * 给pdf生成页码并指定居中
     * @param pdf pdf文件
     * @param document 文档，可以向里面写数据
     * @param a4 纸张属性，主要用于获取宽度
     * @param font 使用的字体
     */
    private void createPageNumber(PdfDocument pdf, Document document, PageSize a4, PdfFont font) {
        int numberOfPages = pdf.getNumberOfPages();
        float x = a4.getWidth() / 2;
        float y = document.getBottomMargin() / 2;
        for (int i = 1; i <= numberOfPages; i++) {
            String text = String.format("第 %s 页 / 共 %s 页", i, numberOfPages);
            document.showTextAligned(new Paragraph(text).setFont(font),
                    x, y, i, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
        }
    }

}
